import babylon from 'babylon'
import types from 'babel-types'

function evaluate(node, scope) {
  const _evalute = visitors[node.type];
  if (!_evalute) {
    throw new Error(`Unknown visitors of ${node.type}`);
  }
  // 递归调用
  return _evalute(node, scope);
}
let visitors = {
  File(node, scope) {
    evaluate(node.program, scope);
  },

  Program(program, scope) {
    for (const node of program.body) {
      evaluate(node, scope);
    }
  },
  ExpressionStatement(node, scope) {
    return evaluate(node.expression, scope);
  },

  CallExpression(node, scope) {
    // 获取调用者对象
    const func = evaluate(node.callee, scope);

    // 获取函数的参数
    const funcArguments = node.arguments.map(arg => evaluate(arg, scope));

    // 如果是获取属性的话: console.log
    if (types.isMemberExpression(node.callee)) {
      const object = evaluate(node.callee.object, scope);
      return func.apply(object, funcArguments);
    }else if(types.isIdentifier(node.callee)){
      func.apply(scope,funcArguments)
    }
  },

  MemberExpression(node, scope) {
    const { object, property } = node;

    // 找到对应的属性名
    const propertyName = property.name;

    // 找对对应的对象
    const obj = evaluate(object, scope);
    // console.log(object);
    
    // 获取对应的值
    const target = obj[propertyName];

    // 返回这个值，如果这个值是function的话，那么应该绑定上下文this
    return typeof target === "function" ? target.bind(obj) : target;
  },
  Identifier(node, scope) {
    // 获取变量的值
    return scope[node.name];
  },

  VariableDeclaration(node,scope){
    const kind = node.kind;
    for (const declaraor of node.declarations) {
      const { name } = declaraor.id; //Identifier.name
      const value = declaraor.init
      ? evaluate(declaraor.init,scope):undefined;
      scope[name] = value;
      
    }
  },
  VariableDeclarator(node, scope) {
    scope[node.id.name] = evaluate(node.init, scope);
  },
  BlockStatement(block,scope){
    for(const node of block.body){
      evaluate(node,scope)
    }

  },

  FunctionDeclaration(node,scope){
    const func = visitors.FunctionExpression(node,scope);
    scope[node.id.name] = func;
  },

  FunctionExpression(node,scope){
    const func = function(){
      evaluate(node.body,scope);
    }
    return func;
  },
  StringLiteral(node) {
    return node.value;
  },

  NumericLiteral(node){
    return node.value;
  },
  
}






const code =`
function test() {
  var name = "hello w2orld";
  console.log(name);
}
test();
`;
const ast = babylon.parse(code);
// console.log(JSON.stringify(ast));

const ctx = { console,Math }
evaluate(ast,ctx );